package com.atguigu.tingshu.common.Login;

import com.atguigu.tingshu.common.constant.RedisConstant;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.result.ResultCodeEnum;
import com.atguigu.tingshu.common.util.AuthContextHolder;
import com.atguigu.tingshu.vo.user.UserInfoVo;
import jakarta.servlet.http.HttpServletRequest;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;

/**
 * @author xiaoxu
 * @description:
 * @date 2024/08/24 08:27
 **/
@Slf4j
@Component
@Aspect
@SuppressWarnings("all")
public class LoginAspect {
    @Autowired
    private RedisTemplate redisTemplate;


    @SneakyThrows
    @Around("@annotation(com.atguigu.tingshu.common.Login.TingqLogin)")
    public Object loginAspect(ProceedingJoinPoint joinPoint) {
        Object object = new Object();
        log.info("前置通知获取请求头判断是否有token:::");

        RequestAttributes attributes = RequestContextHolder.getRequestAttributes();

        ServletRequestAttributes sra = (ServletRequestAttributes) attributes;

        HttpServletRequest request = sra.getRequest();

        String token = request.getHeader("token");

        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        //获取切入点的方法全路径  只要是标注了@TingshuLogin
        Method targetMethod  = signature.getMethod();

        TingqLogin tingqLogin = targetMethod.getAnnotation(TingqLogin.class);

        String loginKey = RedisConstant.USER_LOGIN_KEY_PREFIX + token;
        //根据key查询信息
        UserInfoVo userInfoVo = (UserInfoVo) redisTemplate.opsForValue().get(loginKey);

        //用户需要登录
        if (tingqLogin.required()) {
            //判断此时登录信息
            if (userInfoVo==null){
                throw new GuiguException(ResultCodeEnum.LOGIN_AUTH);
            }
        }
        if (userInfoVo!=null){
            //将用户信息存放到ThreadLocal中
            AuthContextHolder.setUserId(userInfoVo.getId());
        }


        //看redis里面的信息 用户token进行续期
        if (!StringUtils.isEmpty(token)){

            if (userInfoVo!=null){
                //设置token无感刷新
                Long expire = redisTemplate.getExpire(RedisConstant.USER_LOGIN_KEY_PREFIX + token);
                if (expire<3600){
                    redisTemplate.expire(RedisConstant.USER_LOGIN_KEY_PREFIX,7, TimeUnit.DAYS);
                }
                AuthContextHolder.setUserId(userInfoVo.getId());
            }
        }


        //执行目标方法 object返回的对象
        object = joinPoint.proceed(); //切入点对象
        log.info("后置通知逻辑执行");

        //防止内存泄露  ThreadLocal key 是 弱引用 value 是强引用
        AuthContextHolder.removeUserId();


        return object;
    }
}
